package apps

import (
	"context"
	"fmt"
	"github.com/redis/go-redis/v9"
	"main/src/configs"
	"strconv"
	"time"
)

var RedisClient *redis.ClusterClient

func ConnectToCluster() (err error) {
	RedisClient = redis.NewClusterClient(&redis.ClusterOptions{
		Addrs:        configs.Config.RedisAddress,
		Password:     configs.Config.RedisPassword,
		MaxRedirects: 8,

		//只含读操作的命令的"节点选择策略"。默认都是false，即只能在主节点上执行。
		ReadOnly: false, // 置为true则允许在从节点上执行只含读操作的命令
		// 默认false。 置为true则ReadOnly自动置为true,表示在处理只读命令时，可以在一个slot对应的主节点和所有从节点中选取Ping()的响应时长最短的一个节点来读数据
		RouteByLatency: false,
		// 默认false。置为true则ReadOnly自动置为true,表示在处理只读命令时，可以在一个slot对应的主节点和所有从节点中随机挑选一个节点来读数据
		RouteRandomly: false,

		//用户可定制读取节点信息的函数，比如在非集群模式下可以从zookeeper读取。
		//但如果面向的是redis cluster集群，则客户端自动通过cluster slots命令从集群获取节点信息，不会用到这个函数。
		//ClusterSlots: func(c context.Context) (slot []redis.ClusterSlot,err error) {
		//	log.Println()
		//	return slot,err
		//},

		//钩子函数，当一个新节点创建时调用，传入的参数是新建的redis.Client
		NewClient: func(opt *redis.Options) (r *redis.Client) {
			println("database:" + strconv.Itoa(opt.DB))
			return redis.NewClient(opt)
		},

		//钩子函数
		//仅当客户端执行命令需要从连接池获取连接时，如果连接池需要新建连接则会调用此钩子函数
		OnConnect: func(ctx context.Context, conn *redis.Conn) error {
			fmt.Printf("redist Conn = %v\n", conn)
			return nil
		},

		//每一个redis.Client的连接池容量及闲置连接数量，而不是cluterClient总体的连接池大小。实际上没有总的连接池
		//而是由各个redis.Client自行去实现和维护各自的连接池。
		PoolSize:     15, // 连接池最大socket连接数，默认为5倍CPU数， 5 * runtime.NumCPU
		MinIdleConns: 10, //在启动阶段创建指定数量的Idle连接，并长期维持idle状态的连接数不少于指定数量；。

		//命令执行失败时的重试策略
		MaxRetries:      0,                      // 命令执行失败时，最多重试多少次，默认为0即不重试
		MinRetryBackoff: 8 * time.Millisecond,   //每次计算重试间隔时间的下限，默认8毫秒，-1表示取消间隔
		MaxRetryBackoff: 512 * time.Millisecond, //每次计算重试间隔时间的上限，默认512毫秒，-1表示取消间隔

		//超时
		DialTimeout:  5 * time.Second, //连接建立超时时间，默认5秒。
		ReadTimeout:  3 * time.Second, //读超时，默认3秒， -1表示取消读超时
		WriteTimeout: 3 * time.Second, //写超时，默认等于读超时，-1表示取消读超时
		PoolTimeout:  4 * time.Second, //当所有连接都处在繁忙状态时，客户端等待可用连接的最大等待时长，默认为读超时+1秒。

	})

	err = RedisClient.ForEachShard(context.Background(), func(ctx context.Context, shard *redis.Client) error {
		println(shard.Options().Addr)
		return shard.Ping(ctx).Err()
	})
	if err != nil {
		panic(err)
	}

	return nil
}
